import os
import sqlite3
import time
import threading
from pathlib import Path
from typing import List, Dict, Set, Optional, Tuple, Any
import fnmatch
from datetime import datetime
from collections import deque
import sys
import argparse
import ctypes
from ctypes import wintypes
import queue  # 正确的导入方式

# Windows API 函数声明
if os.name == 'nt':
    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
    FindFirstFileW = kernel32.FindFirstFileW
    FindNextFileW = kernel32.FindNextFileW
    FindClose = kernel32.FindClose

    FILE_ATTRIBUTE_DIRECTORY = 0x10
    INVALID_HANDLE_VALUE = -1


    class WIN32_FIND_DATAW(ctypes.Structure):
        _fields_ = [
            ('dwFileAttributes', wintypes.DWORD),
            ('ftCreationTime', wintypes.FILETIME),
            ('ftLastAccessTime', wintypes.FILETIME),
            ('ftLastWriteTime', wintypes.FILETIME),
            ('nFileSizeHigh', wintypes.DWORD),
            ('nFileSizeLow', wintypes.DWORD),
            ('dwReserved0', wintypes.DWORD),
            ('dwReserved1', wintypes.DWORD),
            ('cFileName', wintypes.WCHAR * 260),
            ('cAlternateFileName', wintypes.WCHAR * 14),
        ]


    LPFIND_DATAW = ctypes.POINTER(WIN32_FIND_DATAW)


class FastFileIndexer:
    def __init__(self, db_path: str = "file_index.db"):
        self.db_path = db_path
        self.conn = None
        self.batch_size = 50000
        self.batch_data = []
        self.page_size = 10
        self.search_history = []
        self.init_database()
        self._stop_event = threading.Event()

    def init_database(self):
        """初始化数据库"""
        self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
        self.conn.execute('PRAGMA journal_mode = WAL')
        self.conn.execute('PRAGMA synchronous = NORMAL')
        self.conn.execute('PRAGMA cache_size = -20000')
        self.conn.execute('PRAGMA temp_store = MEMORY')

        cursor = self.conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS files (
                id INTEGER PRIMARY KEY,
                path TEXT UNIQUE,
                name TEXT,
                extension TEXT,
                size INTEGER,
                modified REAL,
                is_dir INTEGER,
                parent_dir TEXT
            )
        ''')

        self.conn.commit()

    def update_index(self, directories: List[str] = None, exclude_patterns: List[str] = None) -> Tuple[int, int]:
        """使用快速方法更新文件索引"""
        if directories is None:
            if os.name == 'nt':
                directories = [os.path.expanduser("~")]
                for drive in ['c:', 'd:', 'e:', 'f:']:
                    drive_path = drive + '\\'
                    if os.path.exists(drive_path):
                        directories.append(drive_path)
            else:
                directories = [os.path.expanduser("~"), "/"]

            directories = [d for d in directories if os.path.exists(d)]

            if not directories:
                directories = [os.getcwd()]

        if exclude_patterns is None:
            exclude_patterns = [
                '**/$RECYCLE.BIN/**', '**/System Volume Information/**',
                '**/Windows/**', '**/Program Files/**', '**/Program Files (x86)/**',
                '**/node_modules/**', '**/venv/**', '**/.git/**', '**/__pycache__/**',
                '**/*.tmp', '**/*.temp', '**/Thumbs.db', '**/desktop.ini'
            ]

        print(f"\n🔄 开始快速更新文件索引...")
        total_start = time.time()

        # 清空现有数据
        cursor = self.conn.cursor()
        cursor.execute('DELETE FROM files')
        self.conn.commit()

        total_files = 0
        total_dirs = 0

        # 使用多线程处理不同目录
        threads = []
        results_queue = queue.Queue()  # 使用 queue.Queue

        for directory in directories:
            if not os.path.exists(directory):
                continue

            thread = threading.Thread(
                target=self._index_directory_fast,
                args=(directory, exclude_patterns, results_queue)
            )
            threads.append(thread)
            thread.start()

        # 等待所有线程完成
        for thread in threads:
            thread.join()

        # 处理结果
        while not results_queue.empty():
            files, dirs = results_queue.get()
            total_files += files
            total_dirs += dirs

        # 提交剩余的批处理数据
        if self.batch_data:
            self._commit_batch()

        # 创建索引
        self._create_indexes()

        total_time = time.time() - total_start

        # 获取新文件数量
        cursor.execute('SELECT COUNT(*) FROM files')
        new_count = cursor.fetchone()[0]

        print(f"\n📊 索引更新完成!")
        print(f"   ⏱️  总耗时: {total_time:.2f}s")
        print(f"   📂 文件数: {total_files}")
        print(f"   📁 目录数: {total_dirs}")
        print(f"   🗃️  总记录: {new_count}")
        if total_time > 0:
            print(f"   ⚡ 平均速度: {(total_files + total_dirs) / total_time:.0f} 项/秒")

        return total_files, total_dirs

    def _index_directory_fast(self, directory: str, exclude_patterns: List[str], results_queue: queue.Queue):
        """快速索引单个目录"""
        files_count = 0
        dirs_count = 0

        try:
            if os.name == 'nt' and hasattr(self, '_walk_windows_fast'):
                files_count, dirs_count = self._walk_windows_fast(directory, exclude_patterns)
            else:
                files_count, dirs_count = self._walk_standard(directory, exclude_patterns)

        except Exception as e:
            print(f"❌ 索引目录 {directory} 时出错: {e}")

        results_queue.put((files_count, dirs_count))

    def _walk_standard(self, root_path: str, exclude_patterns: List[str]) -> Tuple[int, int]:
        """标准文件遍历方法（跨平台）"""
        files_count = 0
        dirs_count = 0

        stack = [root_path]
        while stack and not self._stop_event.is_set():
            current_dir = stack.pop()

            try:
                with os.scandir(current_dir) as entries:
                    for entry in entries:
                        try:
                            if self._should_exclude(entry.path, exclude_patterns):
                                continue

                            if entry.is_dir(follow_symlinks=False):
                                dirs_count += 1
                                stack.append(entry.path)
                            else:
                                files_count += 1

                            # 获取文件信息
                            stat = entry.stat()
                            extension = os.path.splitext(entry.name)[1].lower() if entry.is_file() else ""

                            self.batch_data.append((
                                entry.path, entry.name, extension, stat.st_size,
                                stat.st_mtime, 1 if entry.is_dir() else 0,
                                os.path.dirname(entry.path)
                            ))

                            # 批处理提交
                            if len(self.batch_data) >= self.batch_size:
                                self._commit_batch()

                        except (OSError, PermissionError):
                            continue

            except (OSError, PermissionError):
                continue

        return files_count, dirs_count

    def _should_exclude(self, path: str, patterns: List[str]) -> bool:
        """快速排除检查"""
        normalized_path = path.replace('\\', '/').lower()

        for pattern in patterns:
            normalized_pattern = pattern.replace('\\', '/').lower()

            if '**' in normalized_pattern:
                if fnmatch.fnmatch(normalized_path, normalized_pattern):
                    return True
            else:
                if fnmatch.fnmatch(normalized_path, normalized_pattern):
                    return True
                if fnmatch.fnmatch(os.path.basename(normalized_path), normalized_pattern):
                    return True

        return False

    def _commit_batch(self):
        """提交批处理数据到数据库"""
        if not self.batch_data:
            return

        try:
            cursor = self.conn.cursor()
            cursor.executemany('''
                INSERT OR IGNORE INTO files (path, name, extension, size, modified, is_dir, parent_dir)
                VALUES (?, ?, ?, ?, ?, ?, ?)
            ''', self.batch_data)
            self.conn.commit()
            self.batch_data = []
        except Exception as e:
            print(f"❌ 提交批处理时出错: {e}")
            self.conn.rollback()

    def _create_indexes(self):
        """创建索引"""
        try:
            cursor = self.conn.cursor()
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_name ON files(name)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_extension ON files(extension)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_modified ON files(modified)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_parent_dir ON files(parent_dir)')
            self.conn.commit()
        except Exception as e:
            print(f"❌ 创建索引时出错: {e}")

    def search_files(self, query: str, file_types: List[str] = None, page: int = 1) -> Tuple[List[Dict], int, float]:
        """搜索文件"""
        search_start = time.time()

        cursor = self.conn.cursor()

        conditions = ["name LIKE ?"]
        params = [f"%{query}%"]

        if file_types:
            type_conditions = []
            for file_type in file_types:
                if file_type.startswith('.'):
                    type_conditions.append("extension = ?")
                    params.append(file_type.lower())
                else:
                    type_conditions.append("extension = ?")
                    params.append(f".{file_type.lower()}")

            if type_conditions:
                conditions.append(f"({' OR '.join(type_conditions)})")

        where_clause = " AND ".join(conditions)

        # 获取总数
        count_sql = f'SELECT COUNT(*) FROM files WHERE {where_clause}'
        cursor.execute(count_sql, params)
        total_results = cursor.fetchone()[0]

        if total_results == 0:
            return [], 0, 0

        # 计算分页
        total_pages = (total_results + self.page_size - 1) // self.page_size
        page = max(1, min(page, total_pages))
        offset = (page - 1) * self.page_size

        # 获取当前页数据
        sql = f'''
            SELECT path, name, extension, size, modified, is_dir 
            FROM files 
            WHERE {where_clause}
            ORDER BY 
                CASE 
                    WHEN name LIKE ? THEN 0
                    WHEN name LIKE ? THEN 1
                    ELSE 2
                END,
                modified DESC
            LIMIT ? OFFSET ?
        '''

        search_params = params + [f"{query}%", f"%{query}%", self.page_size, offset]

        cursor.execute(sql, search_params)
        results = cursor.fetchall()

        files = []
        for row in results:
            files.append({
                'path': row[0],
                'name': row[1],
                'extension': row[2],
                'size': self._format_size(row[3]),
                'modified': datetime.fromtimestamp(row[4]).strftime("%Y-%m-%d %H:%M"),
                'is_dir': bool(row[5])
            })

        search_time = time.time() - search_start

        return files, total_pages, search_time

    def _format_size(self, size_bytes: int) -> str:
        """格式化文件大小"""
        if size_bytes == 0:
            return "0B"

        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        while size_bytes >= 1024 and i < len(size_names) - 1:
            size_bytes /= 1024.0
            i += 1

        return f"{size_bytes:.1f}{size_names[i]}"

    def get_stats(self) -> Dict:
        """获取统计信息"""
        cursor = self.conn.cursor()

        cursor.execute('SELECT COUNT(*) FROM files WHERE is_dir = 0')
        file_count = cursor.fetchone()[0]

        cursor.execute('SELECT COUNT(*) FROM files WHERE is_dir = 1')
        dir_count = cursor.fetchone()[0]

        cursor.execute('SELECT COUNT(DISTINCT extension) FROM files WHERE is_dir = 0')
        ext_count = cursor.fetchone()[0]

        cursor.execute('SELECT MAX(modified) FROM files')
        latest_modified = cursor.fetchone()[0]
        latest_time = datetime.fromtimestamp(latest_modified).strftime("%Y-%m-%d %H:%M") if latest_modified else "N/A"

        cursor.execute('SELECT SUM(size) FROM files WHERE is_dir = 0')
        total_size = cursor.fetchone()[0] or 0

        return {
            'total_files': file_count,
            'total_dirs': dir_count,
            'unique_extensions': ext_count,
            'latest_update': latest_time,
            'total_size': self._format_size(total_size)
        }

    def interactive_search(self):
        """交互式搜索界面"""
        print("\n" + "=" * 60)
        print("🔍 文件搜索系统 - 交互模式")
        print("=" * 60)
        print("命令说明:")
        print("  /help     - 显示帮助信息")
        print("  /stats    - 显示统计信息")
        print("  /exit     - 退出程序")
        print("  /page N   - 跳转到第N页")
        print("  /type ext - 按文件类型搜索")
        print("  /clear    - 清除类型筛选")
        print("  搜索词    - 搜索文件")
        print("=" * 60)

        current_page = 1
        current_query = ""
        current_file_types = None

        while True:
            try:
                user_input = input("\n🔍 请输入搜索词或命令: ").strip()

                if not user_input:
                    continue

                if user_input.startswith('/'):
                    command = user_input[1:].lower().split()

                    if not command:
                        continue

                    if command[0] == 'exit':
                        print("👋 再见!")
                        break

                    elif command[0] == 'help':
                        print("\n📖 帮助信息:")
                        print("  /help     - 显示此帮助")
                        print("  /stats    - 显示索引统计")
                        print("  /exit     - 退出程序")
                        print("  /page N   - 跳转到指定页码")
                        print("  /type ext - 按文件类型筛选")
                        print("  /clear    - 清除类型筛选")
                        print("  直接输入文字进行搜索")

                    elif command[0] == 'stats':
                        stats = self.get_stats()
                        print(f"\n📊 索引统计:")
                        print(f"  文件数量: {stats['total_files']}")
                        print(f"  目录数量: {stats['total_dirs']}")
                        print(f"  文件类型: {stats['unique_extensions']} 种")
                        print(f"  总大小: {stats['total_size']}")
                        print(f"  最后更新: {stats['latest_update']}")

                    elif command[0] == 'page' and len(command) > 1:
                        try:
                            new_page = int(command[1])
                            current_page = new_page
                            print(f"📄 跳转到第 {current_page} 页")
                            if current_query:
                                self._display_results(current_query, current_file_types, current_page)
                        except ValueError:
                            print("❌ 页码必须是数字")

                    elif command[0] == 'type' and len(command) > 1:
                        file_type = command[1].lower()
                        if not file_type.startswith('.'):
                            file_type = '.' + file_type
                        current_file_types = [file_type]
                        print(f"📄 筛选文件类型: {file_type}")
                        if current_query:
                            current_page = 1
                            self._display_results(current_query, current_file_types, current_page)

                    elif command[0] == 'clear':
                        current_file_types = None
                        print("✅ 已清除类型筛选")
                        if current_query:
                            current_page = 1
                            self._display_results(current_query, current_file_types, current_page)

                    else:
                        print("❌ 未知命令，输入 /help 查看帮助")

                else:
                    current_query = user_input
                    current_page = 1
                    if user_input not in self.search_history:
                        self.search_history.append(user_input)
                    self._display_results(current_query, current_file_types, current_page)

            except KeyboardInterrupt:
                print("\n👋 再见!")
                break
            except Exception as e:
                print(f"❌ 发生错误: {e}")

    def _display_results(self, query: str, file_types: List[str], page: int):
        """显示搜索结果"""
        print(f"\n🔍 搜索: '{query}'", end="")
        if file_types:
            print(f" [类型: {', '.join(file_types)}]", end="")
        print()

        results, total_pages, search_time = self.search_files(query, file_types, page)

        if not results:
            print("❌ 没有找到匹配的文件")
            return

        print(f"📄 第 {page}/{total_pages} 页 (共 {len(results)} 条结果, 搜索耗时: {search_time:.3f}s)")
        print("-" * 80)

        for i, file in enumerate(results, 1):
            icon = "📁" if file['is_dir'] else "📄"
            print(f"{icon} {(page - 1) * self.page_size + i:2d}. {file['name']}")
            print(f"   📍 路径: {file['path']}")
            if not file['is_dir']:
                print(f"   📦 大小: {file['size']} | 🕒 修改: {file['modified']}")
            print()

        print("-" * 80)
        if total_pages > 1:
            print(f"📄 使用 /page [页码] 切换页面 (1-{total_pages})")

    def stop_indexing(self):
        """停止索引过程"""
        self._stop_event.set()

    def close(self):
        """关闭数据库连接"""
        if self.conn:
            self.conn.close()
            print("✓ 数据库连接已关闭")


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="快速文件索引与搜索系统")
    parser.add_argument("--db", default="file_index.db", help="数据库文件路径")
    parser.add_argument("--dirs", nargs="+", help="要索引的目录列表")
    parser.add_argument("--no-interactive", action="store_true", help="只更新索引，不进入交互模式")
    args = parser.parse_args()

    print("🚀 启动快速文件索引系统...")

    indexer = FastFileIndexer(args.db)

    try:
        print("🔄 正在快速更新索引，请稍候...")
        start_time = time.time()
        files, dirs = indexer.update_index(args.dirs)
        total_time = time.time() - start_time

        print(f"✅ 索引完成，耗时: {total_time:.2f}s")
        print(f"📊 索引结果: {files} 个文件, {dirs} 个目录")

        if files == 0 and dirs == 0:
            print("⚠️  没有索引到任何文件")

        if args.no_interactive:
            indexer.close()
            return

    except KeyboardInterrupt:
        print("\n⏹️  用户中断索引过程")
        indexer.stop_indexing()
    except Exception as e:
        print(f"❌ 索引更新失败: {e}")
        return

    # 进入交互模式
    try:
        indexer.interactive_search()
    except KeyboardInterrupt:
        print("\n👋 再见!")
    finally:
        indexer.close()


if __name__ == "__main__":
    main()